package com.binc.testspring.study.nio.groupchat;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * FileName: GroupChatServer 服务端
 * Autho: binC
 * Date:  2022/4/15 11:50
 */
@Slf4j
public class GroupChatServer {

    Selector selector = null;
    ServerSocketChannel serverSocketChannel = null;
    final static  int port = 19999;

    int len = 1024;


    /**
     * 主函数  启动服务器
     * @param args
     */
    public static void main(String[] args) {
        try {
            GroupChatServer groupChatServer = new GroupChatServer();
            groupChatServer.listen();
        } catch (IOException e) {
            e.printStackTrace();
            log.error("=====服务器启动失败=====");
        }
    }






    //构造函数中做一个初始化
    public GroupChatServer() throws IOException {
        // 得到选择器
        this.selector = Selector.open();
        // 打开serverSocketChannel  并绑定 端口
        this.serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        //channel设置成非阻塞
        serverSocketChannel.configureBlocking(false);
        //将channel注册到selector   监听连接事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

    }

    /**
     * 监听selector中是否有关心的事件触发
     */
    public void listen(){
        try {
            while (true){
                //selector.select() 返回值为已经准备就绪的channel数量    如果为0则一直阻塞
                int readyChannels = selector.select();
                if (readyChannels == 0){
                    continue;
                }
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                //获取selector中所有已就绪的channel  并遍历
                while (iterator.hasNext()) {
                    //判断是否是新的连接
                    SelectionKey key = iterator.next();
                    if (key.isAcceptable()){
                        //获取连接的channel
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        //设置channel为非阻塞
                        socketChannel.configureBlocking(false);
                        //将channel注册到selector   监听读事件   这个时候注册一个bytebuffer进去,否则后边无法使用key.attachments
                        socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(len));
                        log.info("===== 新的连接："+socketChannel.getRemoteAddress()+"=====");
                    }

                    //判断是否有数据可读
                    if (key.isReadable()){
                        readData1(key);
                    }

                   //!!!! 这里很重要,将已经处理过的事件从selector中移除
                    iterator.remove();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();;
        }
    }

    /**
     * 单独写一个方法  用于监听到有消息到达后读取
     * @param key  事件的selectionKey   用这个获取到对应的channel并获取到相关的信息
     */
    public void readData1(SelectionKey key){
            SocketChannel channel = null;
        try {
            //获取channel
            SocketChannel socketChannel = (SocketChannel) key.channel();
            // //获取到对应的buffer
            ByteBuffer buffer = (ByteBuffer) key.attachment();
            //读取数据
            int read = socketChannel.read(buffer);

            if (read > 0 ){
                // buffer.flip();
                log.info("===== 收到消息：{"+socketChannel.getRemoteAddress()+":"+new String(buffer.array())+"} =====");
                /*
                 我这里写的是一个群聊,所以需要将收到的消息转发给其他的客户端  注意排除自己
                 */
                sendMsgToOthers(socketChannel,new String(buffer.array()));

            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("===== 读取数据异常："+e.getMessage()+" =====");
            try {
                key.cancel();
                channel.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }

        }

    }

    public void readData(SelectionKey key) {
        SocketChannel channel = null;
        try {
            //得到 channel
            channel = (SocketChannel) key.channel();
            //创建 buffer
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int count = channel.read(buffer);
            //根据 count 的值做处理
            if (count > 0) {
                //把缓存区的数据转成字符串
                String msg = new String(buffer.array());
                //输出该消息
                System.out.println("form客户端:" + msg);
                //向其它的客户端转发消息(去掉自己),专门写一个方法来处理
                sendMsgToOthers(channel ,msg);
            }
        } catch (IOException e) {
            try {
                System.out.println(channel.getRemoteAddress() + "离线了..");
                //取消注册
                key.cancel();
                //关闭通道
                channel.close();
            } catch (IOException e2) {
                e2.printStackTrace();
            }
        }
    }

    /**-
     * 我这里写的是一个群聊,所以需要将收到的消息转发给其他的客户端  注意排除自己
     * @param self
     * @param
     */
    private void sendMsgToOthers(SocketChannel self, String msg) {
        try {
            //遍历所有注册的channel
            for (SelectionKey key : selector.keys()) {
                //获取channel
                Channel channel = key.channel();
                //如果是自己的channel则跳过
               if( channel instanceof SocketChannel && channel != self){
                   //将消息发送给其他的客户端
                   SocketChannel otherChannel = (SocketChannel) channel;
                   //将消息发送给其他的channel
                   otherChannel.write(ByteBuffer.wrap(msg.getBytes()));
               }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }






}
